


#include <iostream>
#include <set>
#include <cpp/any.hpp>

#include "catch.hpp"




TEST_CASE("any:assign")
{
	using namespace cpp;

	//1 
	any x;
	any y;
	y = x;
	REQUIRE(!x.has_value());
	REQUIRE(!y.has_value());

	y = std::move(x);
	REQUIRE(!x.has_value());
	REQUIRE(!y.has_value());

	{
		//2 
		any x(1);
		any y;
		y = x;
		REQUIRE(x.has_value());
		REQUIRE(y.has_value());

		x = std::move(y);
		REQUIRE(x.has_value());
		REQUIRE(!y.has_value());

		x = y;
		REQUIRE(!x.has_value());
		REQUIRE(!y.has_value());
	}


	{

		any i(cpp::in_place<int>, 1);

		any x;
		REQUIRE(!x.has_value());

		x.assign(9);

		REQUIRE(x.has_value());

		x.reset();
		REQUIRE(!x.has_value());

		x.emplace< std::vector<int> >(std::initializer_list<int>{ 1, 2, 3 });

		REQUIRE(x.has_value());


		auto v = any_cast<std::vector<int> &>(x);

		any y = x;

		y = x;

	}
}

TEST_CASE("any:assign2")
{
	using namespace cpp;

	struct X
	{
		bool moved = false;
		bool moved_from = false;
		X() = default;
		X(const X&) = default;
		X(X&& x) : moved(true) { x.moved_from = true; }
	};

	X x;
	any a1;
	a1 = x;
	REQUIRE(x.moved_from == false);
	any a2;
	a2 = std::move(x);
	REQUIRE(x.moved_from == true);
	REQUIRE(any_cast<X&>(a2).moved == true);
}

TEST_CASE("any:assign3")
{
	using namespace cpp;

	struct X
	{
		int s[100];
		bool moved = false;
		bool moved_from = false;
		X() = default;
		X(const X&) = default;
		X(X&& x) : moved(true) { x.moved_from = true; }
	};

	X x;
	any a1;
	a1 = x;
	REQUIRE(x.moved_from == false);
	any a2;
	a2 = std::move(x);
	REQUIRE(x.moved_from == true);
	REQUIRE(any_cast<X&>(a2).moved == true);

	auto a3 = std::move(a2);
	REQUIRE(!a2.has_value());
	REQUIRE(any_cast<X&>(a3).moved == true);
}
TEST_CASE("any:assign_self")

{
	using namespace cpp;
	static std::set<const void*> live_objects;
	struct A {
		A() { live_objects.insert(this); }
		~A() { live_objects.erase(this); }
		A(const A& a) { REQUIRE(live_objects.count(&a)); live_objects.insert(this); }
	};

	any a;
	a = a;
	REQUIRE(!a.has_value());

	a = A{};
	a = a;
	REQUIRE(a.has_value());

	a.reset();
	REQUIRE(live_objects.empty());



	//2
	{
		struct X {
			any a;
		};

		X x;
		std::swap(x, x); // results in "self-move-assignment" of X::a
		REQUIRE(!x.a.has_value());

		x.a = A{};
		std::swap(x, x); // results in "self-move-assignment" of X::a
		REQUIRE(x.a.has_value());

		x.a.reset();
		REQUIRE(live_objects.empty());
	}

	//3
	{
		any a;
		a.swap(a);
		REQUIRE(!a.has_value());

		a = A{};
		a.swap(a);
		REQUIRE(a.has_value());

		a.reset();
		REQUIRE(live_objects.empty());
	}

	//////////////////////////////////////////////////////////////////////////
	{
		any a;
		a.swap(a);
		REQUIRE(!a.has_value());

		a = A{};
		a.swap(a);
		REQUIRE(a.has_value());

		a.reset();
		REQUIRE(live_objects.empty());
	}


}
TEST_CASE("any:cons")
{
	using namespace cpp;

	{
		any x;
		REQUIRE(!x.has_value());

		any y(x);
		REQUIRE(!x.has_value());
		REQUIRE(!y.has_value());

		any z(std::move(y));
		REQUIRE(!y.has_value());
		REQUIRE(!z.has_value());
	}

	{
		any x(1);
		REQUIRE(x.has_value());

		any y(x);
		REQUIRE(x.has_value());
		REQUIRE(y.has_value());

		any z(std::move(y));
		REQUIRE(!y.has_value());
		REQUIRE(z.has_value());




	}

	struct X
	{
		bool moved = false;
		bool moved_from = false;
		X() = default;
		X(const X&) = default;
		X(X&& x) : moved(true) { x.moved_from = true; }
	};

	X x;
	any a1(x);
	REQUIRE(x.moved_from == false);
	any a2(std::move(x));
	REQUIRE(x.moved_from == true);
	REQUIRE(any_cast<X&>(a2).moved == true);


	{
		any x(1);
		any y;
		x.swap(y);
		REQUIRE(!x.has_value());
		REQUIRE(y.has_value());
		x.swap(y);
		REQUIRE(x.has_value());
		REQUIRE(!y.has_value());

		x.reset();
		REQUIRE(!x.has_value());
	}
}

TEST_CASE("any:type")
{
	using namespace cpp;

	any x;
	REQUIRE(x.type() == typeid(void));
	x = 1;
	REQUIRE(x.type() == typeid(int));
	x = any();
	REQUIRE(x.type() == typeid(void));
}

TEST_CASE("any:any_casts")
{
	using namespace cpp;

	using std::string;
	using std::strcmp;

	// taken from example in N3804 proposal

	any x(5);                                   // x holds int
	REQUIRE(any_cast<int>(x) == 5);              // cast to value
	any_cast<int&>(x) = 10;                     // cast to reference
	REQUIRE(any_cast<int>(x) == 10);

	x = "Meow";                                 // x holds const char*
	REQUIRE(strcmp(any_cast<const char*>(x), "Meow") == 0);
	any_cast<const char*&>(x) = "Harry";
	REQUIRE(strcmp(any_cast<const char*>(x), "Harry") == 0);

	x = string("Meow");                         // x holds string
	string s, s2("Jane");
	s = move(any_cast<string&>(x));             // move from any 
	REQUIRE(s == "Meow");
	any_cast<string&>(x) = move(s2);            // move to any

	REQUIRE(any_cast<string&>(x) == "Jane");

	REQUIRE(any_cast<const string&>(x) == "Jane");

	string cat("Meow");
	const any y(cat);                           // const y holds string
	REQUIRE(any_cast<const string&>(y) == cat);


	{

		using cpp::bad_any_cast;
		any x(1);
		auto p = any_cast<double>(&x);
		REQUIRE(p == nullptr);

		x = 1.0;
		p = any_cast<double>(&x);
		REQUIRE(p != nullptr);

		x = any();
		p = any_cast<double>(&x);
		REQUIRE(p == nullptr);

		try
		{
			any_cast<double>(x);
			REQUIRE(false);
		}
		catch(const bad_any_cast&)
		{

		}

		try
		{
			x.value<double>();
			REQUIRE(false);
		}
		catch(const bad_any_cast&)
		{

		}
	}
}




#if 1

TEST_CASE("unique_any:any_casts")
{
	using namespace cpp;

	{
		unique_any x;
		REQUIRE(!x.has_value());

		unique_any z(std::move(x));
		REQUIRE(!x.has_value());
		REQUIRE(!z.has_value());
	}

	{
		unique_any x(1);
		REQUIRE(x.has_value());


		unique_any z(std::move(x));
		REQUIRE(!x.has_value());
		REQUIRE(z.has_value());
	}

	struct X
	{
		bool moved = false;
		bool moved_from = false;
		X() = default;
		X(const X&) = default;
		X(X&& x) : moved(true) { x.moved_from = true; }
	};

	{
		X x;
		unique_any a1(x);
		REQUIRE(x.moved_from == false);
		unique_any a2(std::move(x));
		REQUIRE(x.moved_from == true);
		REQUIRE(any_cast<X&>(a2).moved == true);
	}
	{
		X x;
		unique_any a1(x);
		REQUIRE(x.moved_from == false);

		REQUIRE(any_cast<X>(std::move(a1)).moved == true);

		REQUIRE(any_cast<X&>(a1).moved_from == true);


		unique_any a2(std::move(x));
		REQUIRE(x.moved_from == true);
		REQUIRE(any_cast<X&>(a2).moved == true);



	}

	{
		unique_any x(1);
		unique_any y;
		x.swap(y);
		REQUIRE(!x.has_value());
		REQUIRE(y.has_value());
		x.swap(y);
		REQUIRE(x.has_value());
		REQUIRE(!y.has_value());

		x.reset();
		REQUIRE(!x.has_value());
	}
}

TEST_CASE("unique_any:any_casts2")

{
	using namespace cpp;

	using std::string;
	using std::strcmp;

	// taken from example in N3804 proposal

	unique_any x(5);                                   // x holds int
	REQUIRE(any_cast<int>(x) == 5);              // cast to value
	any_cast<int&>(x) = 10;                     // cast to reference
	REQUIRE(any_cast<int>(x) == 10);

	x = "Meow";                                 // x holds const char*
	REQUIRE(strcmp(any_cast<const char*>(x), "Meow") == 0);
	any_cast<const char*&>(x) = "Harry";
	REQUIRE(strcmp(any_cast<const char*>(x), "Harry") == 0);

	x = string("Meow");                         // x holds string
	string s, s2("Jane");
	s = move(any_cast<string&>(x));             // move from any 
	REQUIRE(s == "Meow");
	any_cast<string&>(x) = move(s2);            // move to any

	REQUIRE(any_cast<string&>(x) == "Jane");

	REQUIRE(any_cast<const string&>(x) == "Jane");

	string cat("Meow");
	const unique_any y(cat);                           // const y holds string
	REQUIRE(any_cast<const string&>(y) == cat);


	{

		using cpp::bad_any_cast;
		unique_any x(1);
		auto p = any_cast<double>(&x);
		REQUIRE(p == nullptr);

		x = 1.0;
		p = any_cast<double>(&x);
		REQUIRE(p != nullptr);

		x = unique_any();
		p = any_cast<double>(&x);
		REQUIRE(p == nullptr);

		try
		{
			any_cast<double>(x);
			REQUIRE(false);
		}
		catch(const bad_any_cast&)
		{

		}

		try
		{
			x.value<double>();
			REQUIRE(false);
		}
		catch(const bad_any_cast&)
		{

		}
	}
}


#endif